﻿namespace SharpF2.Services
{
	using System;
	using System.Reflection;
	using System.Windows;
	using System.Windows.Markup;

	[MarkupExtensionReturnType(typeof (String))]
	public class LanguageExtension : MarkupExtension
	{
		#region Constructors

		public LanguageExtension(String resourceKey)
		{
			ResourceKey = resourceKey;
		}

		#endregion

		// Objet à mettre à jour lors d'un changement de culture
		// On ne garde qu'une référence faible pour ne pas empêcher que l'objet soit ramassé par le GC

		#region Protected Attributes

		protected WeakReference targetObjectRef;
		// Propriété à mettre à jour lors d'un changement de culture
		protected object targetProperty;

		#endregion

		#region Public Properties

		[ConstructorArgument("resourceKey")]
		public String ResourceKey { get; set; }

		#endregion

		// On ne veut pas que les classes héritées redéfinissent cette méthode, donc on la déclare sealed

		#region Public Members

		public override sealed object ProvideValue(IServiceProvider serviceProvider)
		{
			if (string.IsNullOrEmpty(ResourceKey))
				throw new InvalidOperationException("The ResourceKey property cannot be null or empty");

			// On récupère l'objet et la propriété cible pour pouvoir la mettre à
			// jour lors d'un changement de culture
			var target = serviceProvider.GetService(typeof (IProvideValueTarget)) as IProvideValueTarget;
			if (target != null)
			{
				// En mode design, la cible n'est pas définie : elle ne sera donc pas mise à jour
				if (target.TargetObject != null && target.TargetProperty != null)
				{
					targetObjectRef = new WeakReference(target.TargetObject);
					targetProperty = target.TargetProperty;
					// On s'abonne à l'évènement UICultureChanged pour mettre à jour la propriété
					LanguageService.UICultureChanged += LocManager_UICultureChanged;
				}
			}

			return ProvideValueInternal(serviceProvider);
		}

		#endregion

		// Cette méthode devra être redéfinie par les classes héritées

		#region Private Members

		private void LocManager_UICultureChanged(object sender, EventArgs e)
		{
			Refresh();
		}

		// Cette méthode met à jour la cible pour prendre en compte le changement de culture
		private void Refresh()
		{
			// On vérifie que l'objet à mettre à jour existe encore
			if (!targetObjectRef.IsAlive)
			{
				// L'objet a été ramassé par le GC, on se désabonne de l'évènement UICultureChanged
				LanguageService.UICultureChanged -= LocManager_UICultureChanged;
				return;
			}

			var value = ProvideValueInternal(null);

			// Si targetProperty est une DependencyProperty, targetObject est un DependencyObject
			// Sinon, targetProperty est un PropertyInfo, et targetObject un object quelconque
			if (targetProperty is DependencyProperty)
			{
				var obj = targetObjectRef.Target as DependencyObject;
				var prop = targetProperty as DependencyProperty;
				if (obj != null) obj.SetValue(prop, value);
			}
			else
			{
				var obj = targetObjectRef.Target;
				var prop = targetProperty as PropertyInfo;
				if (prop != null) prop.SetValue(obj, value, null);
			}
		}

		#endregion

		#region Protected Members

		protected object ProvideValueInternal(IServiceProvider serviceProvider)
		{
			return LanguageService.ResourceManager != null
			       	? LanguageService.ResourceManager.GetString(ResourceKey)
			       	: "(ResourceString)";
		}

		#endregion
	}
}